#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>

#include <sys/stat.h>
#include <sys/types.h> //构建 key
// 这两个是创建共享内存的头文件
#include <sys/ipc.h>
#include <sys/shm.h>
#include "Comm.hpp"

const int gdefaultid = -1;
const int gsize = 4096;

const std::string pathname = ".";
const int projid = 0x66; // 这两个是构建key的
const int gmode = 0666;  // 定义一个gmode

#define CREATER "creater" // 用户类型   创建
#define USER "USER"       // 使用者

// // 报错的宏
// #define ERR_EXIT(m)         \
//     do                      \
//     {                       \
//         perror(m);          \
//         exit(EXIT_FAILURE); \
//     } while (0)

class Shm
{
private:
    // 创建的一定要是一个全新的共享内存(不想让别的文件看到   就用私有)   代码和下面优化
    void CreateHelper(int flg) // flg  --->  标志位
    {
        //----------------  这里的k   放在下面的构造函数里  ---------------

        // key_t k = ftok(pathname.c_str(),projid);     //两个参数分别是：路径   值       这个是创建key
        // if(k < 0)
        // {
        //     // perror("ftok");
        //     // exit(EXIT_FAILURE);

        //     ERR_EXIT("ftok");
        // }
        printf("key:0x%x\n", _key);

        // 获取  共享内存
        // 共享内存的生命周期，是随内核的
        // ---------------------  注  意  -----------------------
        // _shmid = shmget(k,_size,IPC_CREAT | IPC_EXCL | gmode);
        _shmid = shmget(_key, _size, flg);
        // 这里需要加上对应的权限（shmget需要最后加上文件权限  0666）  对应的perms 就会变成666     然后结束的时候当关联的时候nattch 就会变成1

        if (_shmid < 0)
        {
            ERR_EXIT("shmget");
        }
        printf("shmid:%d\n", _shmid);
    }

    // 创建新的
    void Create()
    {
        // 创建共享内存   就用Creater   复用上面的代码（因为创建和获取只有传的权限有关）
        CreateHelper(IPC_CREAT | IPC_EXCL | gmode);
    }

    // 关联函数  挂接函数
    void Attach()
    {
        // 挂接
        _start_mem = shmat(_shmid, nullptr, 0);
        // 挂接失败
        if ((long long)_start_mem < 0)
        {
            ERR_EXIT("shmat");
        }
        printf("attach success\n"); // 挂接成功
    }

    // 获取（实际是变相的创建）   usertype是使用者
    void Get()
    {
        CreateHelper(IPC_CREAT); // 获取就只需要传参即可
    }

//去关联
    void Detach()
    {
        int n = shmdt(_start_mem);
        if(n == 0)     //去关联成功
        {
            printf("detach success\n");
        }
    }


    // 删除共享内存
    void Destroy()
    {
        // if (_shmid == gdefaultid)
        //     return; // 表示共享内存没有被建立
    // 对于 Destroy 首先需要去关联
        Detach();    //析构函数销毁共享内存    首先 Detach 去关联
        if(_usertype == CREATER)
        {
            int n = shmctl(_shmid, IPC_RMID, nullptr);
            if (n > 0)
            {
                printf("shmctl delete shm:%d success!\n", _shmid);
            }
            else
            {
                ERR_EXIT("shmctl");
            }
        }
    }

public:
    // 对于这里的构造函数  保证共享内存（相同键值  表明身份  表明想创建还是想获取）
    // 填充两个参数
    Shm(const std::string &pathname, int projid, const std::string usertype)
        : _shmid(gdefaultid),
          _size(gsize),
          _start_mem(nullptr),
          _usertype(usertype)
    {
        _key = ftok(pathname.c_str(), projid); // 两个参数分别是：路径   值       这个是创建key
        if (_key < 0)
        {
            // perror("ftok");
            // exit(EXIT_FAILURE);

            ERR_EXIT("ftok");
        }

        // 构造  上面构造成功  直接创建共享内存   对用户类型做一个判断（类型为CREATE  就创建）
        if (_usertype == CREATER) // 创建者
            Create();
        else if (_usertype == USER) // 使用者
            Get();
        else
        {
        }

        // 创建好共享内存进行挂接
        Attach();
    }

    //     //创建的一定要是一个全新的共享内存(不想让别的文件看到   就用私有)
    //       void Create()
    //       {
    //           key_t k = ftok(pathname.c_str(),projid);     //两个参数分别是：路径   值       这个是创建key
    //           if(k < 0)
    //           {
    //               // perror("ftok");
    //               // exit(EXIT_FAILURE);

    //               ERR_EXIT("ftok");
    //           }
    //           printf("key:0x%x\n",k);
    //           //获取  共享内存
    //           //共享内存的生命周期，是随内核的
    //   // ---------------------  注  意  -----------------------
    //           _shmid = shmget(k,_size,IPC_CREAT | IPC_EXCL | gmode);
    //   //这里需要加上对应的权限（shmget需要最后加上文件权限  0666）  对应的perms 就会变成666     然后结束的时候当关联的时候nattch 就会变成1
    //           if(_shmid < 0)
    //           {
    //               ERR_EXIT("shmget");
    //           }
    //           printf("shmid:%d\n",_shmid);
    //       }

    // 获取共享内存(具有相同的key)
    //  void Get()
    //  {
    //      //这也就是同样的ftok  生成同样的key
    //      key_t k = ftok(pathname.c_str(),projid);
    //      if(k < 0)
    //      {
    //          ERR_EXIT("ftok");
    //      }
    //      printf("key:0x%x\n",k);

    //     //然后就进行 共享内存的获取（不需要创建（后面的权限更改））
    //     _shmid = shmget(k,_size,IPC_CREAT);   //创建 ：IPC_CREAT | IPC_EXCL | 0666
    //     if(_shmid < 0)
    //     {
    //         ERR_EXIT("shmget");
    //     }
    //     printf("shmid:%d\n",_shmid);
    // }

    // 获取起始虚拟地址
    void *VirtualAddr()
    {
        printf("VirtualAddr:%p\n", _start_mem); // 打印出来
        return _start_mem;
    }

    // 获取虚拟地址的size的大小
    int size()
    {
        return _size;
    }

    //属性   获取指定共享内存属性
    void Attr()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid,IPC_STAT,&ds);     //ds:输出型参数
        printf("shm_segsz:%ld\n",ds.shm_segsz);   //段的大小  共享内存大小
        printf("key:0x%x\n",ds.shm_perm.__key);  //key值
    }

    ~Shm()
    {
        // if(_usertype == CREATER)
        std::cout<<_usertype <<std::endl;
            Destroy();   //一切进行封到这个Shm对象中   文件名字也就可以改为（Shm）
    }

private:
    int _shmid;
    int _size;
    key_t _key;            // 用户键值   这样既包含id  也包含key
    void *_start_mem;      // 这个是起始的地址
    std::string _usertype; // 用户类型
};
